/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { List } from '@kit.ArkTS';
import { SocketAddress } from './Socket/SocketAddress';
import { Proxy } from './Socket/Proxy'
import { uri } from '@kit.ArkTS'

/**
 * Selects which proxies to use when connecting to network targets.
 * 
 * This interface allows you to control proxy usage on a per-URI basis. 
 * A proxy selector receives the target URI and returns a list of proxy servers to try.
 * If the first proxy fails, the selector's connectFailed method is called and 
 * the next proxy in the list is tried.
 */
export abstract class ProxySelector {
  private static theProxySelector: ProxySelector | null = null;

  /**
   * Gets the default proxy selector.
   * 
   * Behavior is consistent with Android API 24+:
   * - If a custom ProxySelector was set via setDefault(), returns that selector
   * - Otherwise, returns the system default ProxySelector
   * - The system default almost always returns DIRECT connection (no proxy)
   * 
   * Note: Similar to Android API 24+, even if system-level proxy settings exist,
   * regular applications cannot access them due to security and privacy restrictions.
   * The system ProxySelector will return direct connections in nearly all cases.
   * 
   * @returns The default ProxySelector. Never returns null.
   */
  public static getDefault(): ProxySelector {
    return this.theProxySelector;
  }

  /**
   * Sets the default proxy selector.
   * @param selector - The ProxySelector to set as default
   */
  public static setDefault(selector: ProxySelector | null): void {
    this.theProxySelector = selector;
  }

  /**
   * Selects all the applicable proxies based on the target URI.
   * Returns a list of proxies to try in order. If direct connection is desired,
   * the list should contain Proxy.NO_PROXY.
   * 
   * @param uri - The target URI
   * @returns A list of Proxies. Must not be null or empty.
   * @throws Error if uri is null
   */
  abstract select(uri: uri.URI): List<Proxy>

  /**
   * Called to indicate that a connection could not be established to a proxy.
   * This allows the selector to update its internal state and avoid repeatedly
   * trying the same failed proxy.
   * 
   * @param uri - The target URI
   * @param sa - The socket address of the proxy
   * @param error - The error that occurred
   */
  abstract connectFailed(uri: uri.URI, sa: SocketAddress, error: Error): void
}
